#include <GL/glut.h>
#include <stdlib.h>

float rx, ry, zoom;
int solid, points;
void inicializa (void);
void dibuja(void);
void redimensiona (int, int);
void tecladoEspecial(int, int, int);
void teclado(unsigned char, int, int);

GLUnurbsObj *curva;

GLfloat ptosctl[4][3][3] = {
    {{-8.0, -8.0,  8.0}, {-2.8,  0.0,  0.0}, {-8.0, -8.0, -8.0}},
    {{ 1.0,  9.0,  8.0}, {-1.0, -2.0,  0.0}, { 1.0,  9.0, -8.0}},
    {{-1.0,  9.0,  8.0}, { 1.0, -2.0,  0.0}, {-1.0,  9.0, -8.0}},
    {{ 8.0, -8.0,  8.0}, { 2.8,  0.0,  0.0}, { 8.0, -8.0, -8.0}},
};

GLfloat nodosu[8] = {0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0};
GLfloat nodosv[6] = {0.0, 1.0, 1.0, 2.0, 2.0, 3.0};

int main(int argc, char *argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(500,500);
    glutInitWindowPosition(15, 15);
    glutCreateWindow(argv[0]);
    inicializa();
    
    glutReshapeFunc(redimensiona);
    glutDisplayFunc(dibuja);
    glutSpecialFunc(tecladoEspecial);
    glutKeyboardFunc(teclado);
    
    glutMainLoop();
    return 0;
}
    
void inicializa(void) {
    glClearColor(1.0, 1.0, 1.0, 1.0);
    rx = ry = 0.0;
    zoom = 0.8;
    solid = 0;
    points = 1;
    glEnable(GL_DEPTH_TEST);
    glLineWidth(2);
    glPointSize(7.0);
    curva = gluNewNurbsRenderer();
    gluNurbsProperty(curva, GLU_SAMPLING_TOLERANCE, 20.0);
}

void dibuja(void) {
    int i,j;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(0.0, 0.0, 1.0);
    glPushMatrix();
		//La transformacion de vision
		glRotatef(ry, 0.0, 1.0, 0.0);
		glRotatef(rx, 1.0, 0.0, 0.0);
		glScalef(zoom, zoom, zoom);
      
		//Pinta la superficie de NURBS
		gluNurbsProperty(curva, GLU_DISPLAY_MODE, solid ? GLU_FILL : GLU_OUTLINE_POLYGON);

		gluBeginSurface(curva);
			gluNurbsSurface(curva, 8, nodosu, 6, nodosv, 3 * 3, 3, &ptosctl[0][0][0], 4, 3, GL_MAP2_VERTEX_3);
		gluEndSurface(curva);
      
		//Pinta los puntos de control
		if (points) {
			glColor3f(1.0, 0.0, 0.0);
			glBegin(GL_POINTS);
			for (i = 0; i < 4; i++) {
				for (j = 0; j < 4; j++) {
					glVertex3fv(&ptosctl[i][j][0]);
				}
			}
			glEnd();
		}

    glPopMatrix();
    
    glutSwapBuffers();
}

void redimensiona (int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-12.0, 12.0, -12.0, 12.0, -12.0, 12.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void tecladoEspecial (int key, int mx, int my) {
	
	switch(key) {
		
		case GLUT_KEY_UP:
			rx -= 10.0;
		break;
		
		case GLUT_KEY_DOWN:
			rx += 10.0;
		break;
		
		case GLUT_KEY_LEFT:
			ry -= 10.0;
		break;
		
		case GLUT_KEY_RIGHT:
			ry += 10.0;
		break;
		
		case GLUT_KEY_PAGE_UP:
			zoom += 0.1;
		break;
		
		case GLUT_KEY_PAGE_DOWN:
			zoom -= 0.1;
		break;
		
		default: 
		break;
	}
	
	glutPostRedisplay();
}

void teclado (unsigned char key, int mx, int my) {
	switch(key) {
		
		case 27:
			exit(0);
		break;
		
		case 's':
		case 'S':
			solid = (!solid);
		break;
		
		case 'p':
		case 'P':
			points = (!points);
		break;

		default: 
		break;
	}
	
	glutPostRedisplay();
}
